#include "codec.h"
#include "socket_httpserver.h"
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include "../main_app.h"

 MainApp* ptr = NULL;
CHttpReq::CHttpReq(STRCPP rootdir):m_rootdir(rootdir)
{
    recv_buff_.clear();
    send_buff_.clear();
	m_reqhead.clear();
	m_reqbody.clear();
	m_reqinfo.clear();
	m_reqstat = 1;
	m_reqsize = 0;
	m_reqfile.clear();
	m_method.clear();
	m_query.clear();
    m_parser = NULL;    
}

CHttpReq::~CHttpReq()
{

}

//打印http请求内容
VOID CHttpReq::show()
{
	cout << "********************************************" << endl;
    cout << "http head"<<endl;
    cout << m_reqhead;
    cout << "http body"<<endl;
	cout << m_reqbody;
	cout << endl;
	cout << "********************************************" << endl;
	cout << "- method: " << m_method << endl;
    cout << "- requrl: " << req_url_ << endl;
	cout << "- reqfile:" << m_reqfile << endl;
    cout << "- version:" << version_ << endl;
	cout << "--------------------------------------------" << endl;

}

//深copy
VOID* CHttpReq::clone()
{
	CHttpReq* req = new CHttpReq("");
	*req = *this;
    req->init_http();
    return req;
}

void CHttpReq::init_http()
{    
    m_parser = new http_parser;
    http_parser_init(m_parser, HTTP_REQUEST);
    m_parserSettings.on_message_begin = MessageBegin;
    m_parserSettings.on_path = Path;
    m_parserSettings.on_query_string = 0;
    m_parserSettings.on_url = Url;
    m_parserSettings.on_fragment = Fragment;
    m_parserSettings.on_header_field = HeaderField;
    m_parserSettings.on_header_value = HeaderValue;
    m_parserSettings.on_headers_complete = HeadersComplete;
    m_parserSettings.on_body = Body;
    m_parserSettings.on_message_complete = MessageComplete;
    m_parser->data = this;
}

/* 接受一次http请求
   请求行     reqline  方法 空格 URL 空格 协议版本
   请求头部   headers  头字段key 冒号: 值value
   空行       ""       CRLF
   请求消息体 reqbody
*/
BOOL CHttpReq::recv_http()
{
    if(readinfo(recv_buff_))
    {
        printf("------- http recv  %s \n", recv_buff_.c_str());
        http_parser_execute(m_parser, &m_parserSettings, recv_buff_.c_str(), recv_buff_.size());
        return TRUE;
    }
    return FALSE;
}

VOID CHttpReq::response_info(const STRCPP& info, const STRCPP& code)
{
	OSTREAM buffer;
	buffer << HTTP_HEAD_VERSION << code << " " << CHttpInfo::respinfo(code) << "\r\n";
	buffer << "Server: " << HTTP_SERVER_NAME + "\r\n";
	buffer << "Date: " << getgmttime(time(NULL)) + "\r\n";
	buffer << "Content-Type: " << "text/html" << "\r\n";
	buffer << "Content-Length: " << info.size() << "\r\n";
	buffer << "\r\n";
	sendinfo(buffer.str());
	sendinfo(info);
}

// 响应文件 
// 如果包含If-Modified-Since 并且和文件的最后修改时间相同 则应答304 不带文件内容 否则应答文件内容 并设置Last-Modified
VOID CHttpReq::response_file(const STRCPP& filename)
{
	IFSTREAM filestream;
	SSTREAM  headstream;	
	SSTREAM  filebuff;
	filestream.open(filename.c_str(), ios::in | ios::binary);
	headstream << HTTP_HEAD_VERSION << "200" << " " << CHttpInfo::respinfo("200") << "\r\n";
	headstream << "Server: " << HTTP_SERVER_NAME + "\r\n";
	headstream << "Date: " << getgmttime(time(NULL)) + "\r\n";
	headstream << "Content-Type: " << CHttpInfo::mimetype(filename) << "\r\n";
	filebuff << filestream.rdbuf();
	headstream << "Content-Length: " << filebuff.str().size() << "\r\n";
	headstream << "\r\n";
    if(!sendinfo(headstream.str())) {
        cout << "send head failed! filename: "<<filename.c_str()<<endl;
    }

    if(!sendinfo(filebuff.str())) {
        cout << "send content failed! filename: "<<filename.c_str()<<endl;
    }
}

VOID CHttpReq::response_path(const STRCPP& path)
{
	STRCPP  fileList;
	DIR*    dir  = NULL;
	DIRENT* item = NULL;

	dir = opendir(path.c_str());
	while(NULL != (item = readdir(dir)))
	{
		fileList += "<a href='";
		fileList += item->d_name;
		fileList += "'>";
		fileList += item->d_name;
		fileList += "</a></br>";
	}
	closedir(dir);
    response_info(fileList);
}

BOOL CHttpReq::process()
{
    //show();
     if(("GET" == m_method && m_query != "") || ("POST" == m_method))
	{

		process_POST();		
	}
	else
	{
        response_info("success.");
        //response_info("501", m_method + "Not Implement");
	}
	return TRUE;
}

VOID CHttpReq::process_POST()
{
	process_CGI();
}

VOID CHttpReq::process_CGI()
{
	FILEFD input[2]  = {0};
	FILEFD output[2] = {0};
	PID    pid;
	//创建管道和子进程
	if(pipe(input) < 0 || pipe(output) < 0 || (pid = fork()) < 0)
	{
        return response_info("POST_webcgi pipe or fork failed","501");
	}
	//子进程执行cgi脚本
	if(0 == pid)
	{
		dup2(input[FDREAD], STDIN);
		dup2(output[FDWRITE], STDOUT);
		close(input[FDWRITE]);
		close(output[FDREAD]);
		process_CGI_setallenv();
		process_CGI_runcgi();
		exit(0);
	}
	//父进程读取cgi的输出 应答客户端
	else
	{
		PSTAT st;
		close(input[FDREAD]);
		close(output[FDWRITE]);
		process_CGI_writereqbody(input[FDWRITE], m_reqbody);
		process_CGI_readresponse(output[FDREAD]);
		close(input[FDWRITE]);
		close(output[FDREAD]);
		waitpid(pid, &st, 0);
	}
}

//需要循环写 待实现
VOID CHttpReq::process_CGI_writereqbody(FILEFD fd, const STRCPP& reqbody)
{
	write(fd, reqbody.c_str(), reqbody.size());
}

VOID CHttpReq::process_CGI_readresponse(FILEFD fd)
{
	CHAR  ch;
	while(0 < read(fd, &ch, 1))
	{
		cout << ch;
		sendinfo(STRCPP(&ch, 1));
	}	
}


VOID CHttpReq::process_CGI_setoneenv(const STRCPP& key, const WORD32& val)
{
	SSTREAM valstr;
	valstr << val;
	setenv(key.c_str(), valstr.str().c_str(), 1);
}
VOID CHttpReq::process_CGI_setoneenv(const STRCPP& key, const STRCPP& val)
{
	setenv(key.c_str(), val.c_str(), 1);
}


VOID CHttpReq::process_CGI_setallenv()
{
	/*
	CGI环境变量名称		说明
	REQUEST_METHOD		请求类型，如“GET”或“POST”
    STRMAP::value_type 		被发送数据的类型
	CONTENT_LENGTH		客户端向标准输入设备发送的数据长度，单位为字节
	QUERY_STRING		查询参数，如“id=10010&sn=liigo”
	SCRIPT_NAME 		CGI脚本程序名称
	PATH_INFO 			CGI脚本程序附加路径
	PATH_TRANSLATED 	PATH_INFO对应的绝对路径
	REMOTE_ADDR 		发送此次请求的主机IP
	REMOTE_HOST 		发送此次请求的主机名
	REMOTE_USER 		已被验证合法的用户名
	REMOTE_IDENT 		WEB服务器的登录用户名
	AUTH_TYPE 			验证类型
	GATEWAY_INTERFACE 	服务器遵守的CGI版本，如：CGI/1.1
	SERVER_NAME 		服务器主机名、域名或IP
	SERVER_PORT 		服务器端口号
	SERVER_PROTOCOL 	服务器协议，如：HTTP/1.1
	DOCUMENT_ROOT 		文档根目录
	SERVER_SOFTWARE 	服务器软件的描述文本
	HTTP_ACCEPT 		客户端可以接收的MIME类型，以逗号分隔
	HTTP_USER_AGENT 	发送此次请求的web浏览器
	HTTP_REFERER 		调用此脚本程序的文档
	HTTP_COOKIE			获取COOKIE键值对，多项之间以分号分隔，如：key1=value1;key2=value2
	*/
	process_CGI_setoneenv("REQUEST_METHOD",   m_method);
    process_CGI_setoneenv("STRMAP::value_type",     m_reqinfo["Content-Type"]);
	process_CGI_setoneenv("CONTENT_LENGTH",   m_reqsize);
	process_CGI_setoneenv("QUERY_STRING",     m_query);
	process_CGI_setoneenv("SCRIPT_NAME",      m_reqfile);
    process_CGI_setoneenv("REMOTE_ADDR",      peer_addr_.ip);
    process_CGI_setoneenv("SERVER_NAME",      local_addr_.addr());
    process_CGI_setoneenv("SERVER_PORT",      local_addr_.portstr());
	process_CGI_setoneenv("SERVER_PROTOCOL",  HTTP_HEAD_VERSION);
	process_CGI_setoneenv("DOCUMENT_ROOT",    m_rootdir);
	process_CGI_setoneenv("SERVER_SOFTWARE",  HTTP_SERVER_NAME);
	process_CGI_setoneenv("HTTP_ACCEPT",      m_reqinfo["Accept"]);
	process_CGI_setoneenv("HTTP_USER_AGENT",  m_reqinfo["User-Agent"]);
	process_CGI_setoneenv("HTTP_REFERER",     m_reqinfo["Referer"]);
	process_CGI_setoneenv("HTTP_COOKIE",      m_reqinfo["Cookie"]);
}


VOID CHttpReq::process_CGI_runcgi()
{
	if(0 != access(m_reqfile.c_str(), X_OK))
	{
        return response_info("CGI script is not executable, name:" + m_reqfile,"403");
	}
	STRVEC strvec;
	strsplit(m_reqfile, ".", strvec);
	if(1 < strvec.size() && "py" == strvec[strvec.size() - 1])
	{
		execl("/usr/bin/python", 
			  "/usr/bin/python", 
			  m_reqfile.c_str(), 
			  NULL);
	}
	else
	{
		execl(m_reqfile.c_str(), 
			  m_reqfile.c_str(), 
			  NULL);
	}
}

int CHttpReq::MessageBegin(http_parser *parser)
{
    CHttpReq *req = (CHttpReq *)parser->data;
    if(req) {
        //hi_trace("******");
        req->req_path_ = "";
        req->req_url_ = "";
        req->req_action_ = "";
        req->req_params_ = "";
        req->m_currentHeaders.clear();
    }
    return 0;
}

void CHttpReq::socketDisconnected()
{
    // m_needDelete = true;
}

void CHttpReq::trim(STRCPP &s)
{//去掉字符串s首尾两端的空白
   if (s.empty())
   {
       return ;
   }
   s.erase(0,s.find_first_not_of(" "));
   s.erase(s.find_last_not_of(" ") + 1);
}

void CHttpReq::parase_path(STRCPP path)
{
    req_path_ = string(path);

    int pos = path.find("HTTP/");
    if(string::npos != pos) {
        //解析url
        req_url_ = path.substr(0,pos);
        trim(req_url_);
        //解析action
        pos = req_url_.find("?");
        if(string::npos != pos) {
            req_action_ = req_url_.substr(0,pos);
        }
        else {
            req_action_ = req_url_;
        }
        //解析params
        if(string::npos != pos) {
            req_params_ = req_url_.substr(pos+1);
        }
        else {
            req_params_ = "";
        }
    }
   // hi_trace("http request path: %s",req_path_.c_str());
    // hi_trace("req: %d url: %s action: %s params: %s", this, req_url_.c_str(), req_action_.c_str(), req_params_.c_str());
}

int CHttpReq::get_content_length()
{
    int len = 0;
    STRVEC strvec;
    strvec.clear();
    strsplit(req_path_, "\n", strvec);
    for(int i = 0; i<strvec.size();i++) {
        STRCPP str = strvec[i];
        if(string::npos != str.find("Content-Length")) {
            int npos = str.find(":");
            if(string::npos != npos) {
                str = str.substr(npos+1);
                trim(str);
                len = atoi(str.c_str());
                break;
            }
        }
    }
    //hi_trace("content-length: %d",len);
    return len;
}

bool CHttpReq::req_http_html()
{
    if(req_action_ == "/") {
        req_action_ = "/live.html";
    }

    STRCPP req_file =  m_rootdir + req_action_;
    if(access(req_file.c_str(), F_OK) == 0) {
        //请求文件
        response_file(req_file);
        return true;
    }

    DIR *dir = NULL;
    if  ((dir = opendir(req_file.c_str())) != NULL)
    {//请求目录
         response_path(req_file);        
         closedir(dir);
         return true;
    }
    return false;
}

int CHttpReq::HeadersComplete(http_parser *parser)
{       
    CHttpReq *req = (CHttpReq *)parser->data;
    if(req) {
        //设置命令
        req->m_method = http_method_str((enum http_method)parser->method);
        //设置http版本
        stringstream ss;
        ss<<parser->http_major<<"."<<parser->http_minor;
        req->version_ = ss.str();

        // Insert last remaining header
        req->m_currentHeaders[req->m_currentHeaderField] = req->m_currentHeaderValue;
       
        if(ptr) {
            ptr->proc_http_msg(req);
        }
    }
    return 0;
}

int CHttpReq::MessageComplete(http_parser *parser)
{    
    return 0;
}

int CHttpReq::Path(http_parser *parser, const char *at, size_t length)
{    
    CHttpReq *req = (CHttpReq *)parser->data;
    if(req) {        
        req->parase_path(string(at));
    }

    return 0;
}

int CHttpReq::QueryString(http_parser *parser, const char *at, size_t length)
{
   // hi_trace("******");
    CHttpReq *req = (CHttpReq *)parser->data;
    return 0;
}

int CHttpReq::Url(http_parser *parser, const char *at, size_t length)
{
   // hi_trace("******");
    return 0;
}

int CHttpReq::Fragment(http_parser *parser, const char *at, size_t length)
{
   // hi_trace("******");
    // TODO: 实现必要的操作
    return 0;
}

int CHttpReq::HeaderField(http_parser *parser, const char *at, size_t length)
{
   // hi_trace("******");
    CHttpReq *req = (CHttpReq *)parser->data;
    if(req) {
        // 插入我们刚才已经解析过的头
        // 插入到map中
        if( !req->m_currentHeaderField.empty() && !req->m_currentHeaderValue.empty() )
        {
            // header 的名字必须全部小写
            req->m_currentHeaders[req->m_currentHeaderField] = req->m_currentHeaderValue;
            //清空下次备用
            req->m_currentHeaderField.clear();
            req->m_currentHeaderValue.clear();
        }

        req->m_currentHeaderField += string(at);
    }

    return 0;
}

int CHttpReq::HeaderValue(http_parser *parser, const char *at, size_t length)
{
    //hi_trace("******");
    CHttpReq * req= (CHttpReq *)parser->data;
    if(req) {
        req->m_currentHeaderValue += string(at);
    }
    return 0;
}

int CHttpReq::Body(http_parser *parser, const char *at, size_t length)
{
    //body数据
//    hi_error("body  %lld  %d  %s  ", parser->content_length, length, at);
    // MainApp* ptr = (MainApp*)NetCenter::instance()->get_context();
    // if(ptr) {
    //     ptr->on_write_file(parser, STRCPP(at,length));
    // }

    return 0;
}

//http相应头中Content-Type 根据文件的扩展名获取
static STRMAP::value_type g_mimetype[] = 
{
    STRMAP::value_type(""    , "text/plain"),
    STRMAP::value_type("txt" , "text/plain"),
    STRMAP::value_type("html", "text/html; charset=utf-8"),
    STRMAP::value_type("htm" , "text/html; charset=utf-8"),
    STRMAP::value_type("css" , "text/css; charset=utf-8"),
    STRMAP::value_type("js"  , "application/x-javascript; charset=utf-8"),
    STRMAP::value_type("xml" , "text/xml"),
    STRMAP::value_type("xsl" , "text/xml"),
    STRMAP::value_type("jpg" , "image/jpeg"),
    STRMAP::value_type("png" , "image/png"),
    STRMAP::value_type("jpeg", "image/jpeg"),
    STRMAP::value_type("bmp" , "image/bmp"),
    STRMAP::value_type("gif" , "image/gif"),
    STRMAP::value_type("tiff", "image/tiff"),
    STRMAP::value_type("zip" , "application/zip"),
    STRMAP::value_type("pdf" , "application/pdf"),
    STRMAP::value_type("doc" , "application/msword"),
    STRMAP::value_type("xls" , "application/vnd.ms-excel"),
    STRMAP::value_type("ppt" , "application/vnd.ms-powerpoint"),
    STRMAP::value_type("swf" , "application/x-shockwave-flash"),
    STRMAP::value_type("wav" , "audio/x-wav"),
    STRMAP::value_type("avi" , "video/x-msvideo"),
};
static const WORD32 g_mimesize = sizeof(g_mimetype)/sizeof(STRMAP::value_type);
STRMAP CHttpInfo::m_mimetype(g_mimetype, g_mimetype+g_mimesize);

//http相应头信息
static STRMAP::value_type g_respinfo[] = 
{
	STRMAP::value_type("100", "Continue"),
	STRMAP::value_type("101", "Switching Protocols"),
	STRMAP::value_type("200", "OK"),
	STRMAP::value_type("201", "Created"),
	STRMAP::value_type("202", "Accepted"),
	STRMAP::value_type("203", "Non-Authoritative Information"),
	STRMAP::value_type("204", "No Content"),
	STRMAP::value_type("205", "Reset Content"),
	STRMAP::value_type("206", "Partial Content"),
	STRMAP::value_type("300", "Multiple Choices"),
	STRMAP::value_type("301", "Moved Permanently"),
	STRMAP::value_type("302", "Found"),
	STRMAP::value_type("303", "See Other"),
	STRMAP::value_type("304", "Not Modified"),
	STRMAP::value_type("305", "Use Proxy"),
	STRMAP::value_type("307", "Temporary Redirect"),
	STRMAP::value_type("400", "Bad Request"),
	STRMAP::value_type("401", "Unauthorized"),
	STRMAP::value_type("402", "Payment Required"),
	STRMAP::value_type("403", "Forbidden"),
	STRMAP::value_type("404", "Not Found"),
	STRMAP::value_type("405", "Method Not Allowed"),
	STRMAP::value_type("406", "Not Acceptable"),
	STRMAP::value_type("407", "Proxy Authentication Required"),
	STRMAP::value_type("408", "Request Timeout"),
	STRMAP::value_type("409", "Conflict"),
	STRMAP::value_type("410", "Gone"),
	STRMAP::value_type("411", "Length Required"),
	STRMAP::value_type("412", "Precondition Failed"),
	STRMAP::value_type("413", "Request Entity Too Large"),
	STRMAP::value_type("414", "Request-URI Too Long"),
	STRMAP::value_type("415", "Unsupported Media Type"),
	STRMAP::value_type("416", "Requested Range Not Satisfiable"),
	STRMAP::value_type("417", "Expectation Failed"),
	STRMAP::value_type("500", "Internal Server Error"),
	STRMAP::value_type("501", "Not Implemented"),
	STRMAP::value_type("502", "Bad Gateway"),
	STRMAP::value_type("503", "Service Unavailable"),
	STRMAP::value_type("504", "Gateway Timeout"),
	STRMAP::value_type("505", "HTTP Version Not Supported"),
};
static const WORD32 g_respsize = sizeof(g_respinfo)/sizeof(STRMAP::value_type);
STRMAP CHttpInfo::m_respinfo(g_respinfo, g_respinfo+g_respsize);

STRCPP CHttpInfo::mimetype(const STRCPP& filename)
{
	STRVEC namevec;
	STRCPP fileext;
	strsplit(filename, ".", namevec);
	if(1 < namevec.size())
	{
		fileext = namevec[namevec.size()-1];
	}

	STRMAP::iterator iter = m_mimetype.find(fileext);
	if(iter == m_mimetype.end())
	{
        //hi_trace("filename: %s fileext: %s", filename.c_str(),fileext.c_str());
		return "application/octet-stream";
	}
	return iter->second;
}

STRCPP CHttpInfo::respinfo(const STRCPP& respcode)
{
	STRMAP::iterator iter = m_respinfo.find(respcode);
	if(iter == m_respinfo.end())
	{
		return "??? response code not found";
	}
	return iter->second;
}

/* 多线程多链接 阻塞*/
VOID* CHttpServer::callback(VOID* args)
{
    CHttpReq* req = (CHttpReq*)args;
    while(NULL != req)
    {
        if(INVALIDFD != req->peerfd())
        {
            if(req->recv_http())
            {                
            
            }
            else {
                //hi_trace("delete req: %d----0",req);
                delete req;
                req = NULL;
            }
        }
        else
        {
            //cout<<"1----exit req thread"<<req->peerfd()<<endl;
            //hi_trace("delete req: %d----1",req);
            delete req;
            req = NULL;
        }
    }
    //cout<<"exit req thread"<<endl;
    return NULL;
}

VOID CHttpServer::working()
{
    while(NULL != m_reqtype)
    {
        CHttpReq* req = (CHttpReq*)m_reqtype->clone();
        if(NULL != req)
        {
            if(req->activate(m_reqblock))
            {
                //hi_trace(".....new req....%d",req);
                THREAD tid;
                pthread_create(&tid, NULL, callback, req);
                pthread_detach(tid); //默认创建的线程是可结合的(joinable) 必须被其他线程回收(调用pthread_join)  或 调用pthread_detach使之分离
            }
            else
            {
                //hi_trace(".....delete req....%d",req);
                delete req;
            }
        }
    }
}

void CHttpServer::start_http(void* context)
{
    is_run_ = true;
    start("http server");
    ptr = (MainApp*)context;
}

void CHttpServer::process()
{
    while (is_run_) {
       startup();
    }
}


